package com.example.controller;

import com.alibaba.fastjson.JSONObject;
import com.example.service.CommissionService;
import com.example.service.PayInfoService;
import com.example.service.UserService;
import com.example.shiro.SysUserEntity;
import com.example.shiro.util.SHA256Util;
import com.example.shiro.util.ShiroUtils;
import com.example.utils.*;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import sun.misc.BASE64Decoder;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * @description:
 * @author: xxx
 * @create: 2022/4/12 15:21
 */
@RestController
@RequestMapping("/oauth")
public class OauthController {
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private UserService userService;
    @Autowired
    private PayInfoService payInfoService;
    @Autowired
    private CommissionService commissionService;
    @Value("${file.parentPath}")
    private String parentPath;
    @Value("${file.url}")
    private String url;

    @Value("${sms.key}")
    private String appKey;
    @Value("${sms.secret}")
    private String appSecret;
//    @Autowired
//    private RestTemplate restTemplate;
    //无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
    private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
    //无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
    private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";
    /**
     * 未登录
     *
     * @Author Sans
     * @CreateTime 2019/6/20 9:22
     */
    @RequestMapping("/unauth")
    public Result unauth() {
        return ResultUtil.error(403,"未登录");
    }
    /**
     * 未登录
     *
     * @Author Sans
     * @CreateTime 2019/6/20 9:22
     */
    @RequestMapping("/unauthTest")
    public Result unauthTest() {
        return ResultUtil.error(401,"画室已冻结");
    }
    @RequestMapping("/logout")
    public Result logout() {
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        System.out.println("退出："+session.toString());
        if (subject.isAuthenticated()) {
            subject.logout();
        }
        Map<String, Object> map = new HashMap<>();
        map.put("msg", "退出成功");
        return ResultUtil.success(map);
    }
    @RequestMapping("/login")
    public Result login(@RequestBody Map<String, Object> map){

        String name = map.get("name").toString();
        String password = map.get("password").toString();
        String verifyCode = map.get("verifyCode").toString();
        //3.获取生成的验证码信息
        String uuid = map.get("uuid").toString();
        if (redisUtil.get("verifyCode:"+uuid) != null) {
            String key = "verifyCode:" + uuid;
            String verifyCode_redis = redisUtil.get(key).toString().toLowerCase();
            //由于redis存储了验证码信息,所以当跳转后在浏览器点返回时候,不改变验证码又可以登录了.所以要在获取后立即删除
            redisUtil.delete(key);
            if (verifyCode != null && verifyCode.toLowerCase().equals(verifyCode_redis)) {
                SysUserEntity userEntity = userService.getUserByName(name);
                //如果mapUser不为空，已经注册
                if(userEntity!=null) {
                    Map<String, Object> mapNew = new HashMap<>();
                    try {
                        //验证身份和登陆
                        Subject subject = SecurityUtils.getSubject();
                        UsernamePasswordToken token = new UsernamePasswordToken(name, password);
                        //进行登录操作
                        subject.login(token);
                        //保存登陆信息到session
                        Session session = ShiroUtils.getSession();
                        Map<String, Object>  userInfo =  userService.selectUserByUserName(name);
                        userInfo.put("token", ShiroUtils.getSession().getId().toString());
                        session.setAttribute("userInfo",userInfo);
                        mapNew.put("userInfo",userInfo);

                        return ResultUtil.success(mapNew);
                    } catch (IncorrectCredentialsException e) {
                        return ResultUtil.error(500,"密码错误");
                    } catch (LockedAccountException e) {
                        return ResultUtil.error(500,"登录失败，该用户已被冻结");
                    } catch (AuthenticationException e) {
                        return ResultUtil.error(500,"该用户不存在");
                    } catch (Exception e) {
                        return ResultUtil.error(500,"未知异常");
                    }
                }else {//否则用户不存在
                    return ResultUtil.error(500,"用户不存在");
                }
            }else{
                return ResultUtil.error(500,"验证码错误");
            }
        }else{
            return ResultUtil.error(500,"验证码错误");
        }


    }
    @RequestMapping("/loginNew")
    public Result loginNew(@RequestBody Map<String, Object> map){

        String name = map.get("name").toString();
        String password = map.get("password").toString();
        String verifyCode = map.get("verifyCode").toString();
        //3.获取生成的验证码信息
        String uuid = map.get("uuid").toString();
        if (redisUtil.get("verifyCode:"+uuid) != null) {
            String key = "verifyCode:" + uuid;
            String verifyCode_redis = redisUtil.get(key).toString().toLowerCase();
            //由于redis存储了验证码信息,所以当跳转后在浏览器点返回时候,不改变验证码又可以登录了.所以要在获取后立即删除
            redisUtil.delete(key);
            if (verifyCode != null && verifyCode.toLowerCase().equals(verifyCode_redis)) {
                SysUserEntity userEntity = userService.getUserByName(name);
                //如果mapUser不为空，已经注册
                if(userEntity!=null) {
                    if (!userEntity.getType().equals("t")) {//不是超管
                        String code = map.get("code").toString();
                        Map<String, String>  codeMap = redisUtil.getMap("code:"+name);
                        if (codeMap != null && codeMap.get(name).equals(code)){
                            redisUtil.delete("code:"+name);
                        }else{
                            return ResultUtil.error(500,"短信验证码错误！");
                        }
                    }
                    Map<String, Object> mapNew = new HashMap<>();
                    try {
                        //验证身份和登陆
                        Subject subject = SecurityUtils.getSubject();
                        UsernamePasswordToken token = new UsernamePasswordToken(name, password);
                        //进行登录操作
                        subject.login(token);
                        //保存登陆信息到session
                        Session session = ShiroUtils.getSession();
                        Map<String, Object>  userInfo =  userService.selectUserByUserName(name);
                        userInfo.put("token", ShiroUtils.getSession().getId().toString());
                        session.setAttribute("userInfo",userInfo);
                        mapNew.put("userInfo",userInfo);

                        return ResultUtil.success(mapNew);
                    } catch (IncorrectCredentialsException e) {
                        return ResultUtil.error(500,"密码错误");
                    } catch (LockedAccountException e) {
                        return ResultUtil.error(500,"登录失败，该用户已被冻结");
                    } catch (AuthenticationException e) {
                        return ResultUtil.error(500,"该用户不存在");
                    } catch (Exception e) {
                        return ResultUtil.error(500,"未知异常");
                    }
                }else {//否则用户不存在
                    return ResultUtil.error(500,"用户不存在");
                }
            }else{
                return ResultUtil.error(500,"验证码错误");
            }
        }else{
            return ResultUtil.error(500,"验证码错误");
        }
    }
    /**
     * 生成6位随机数验证码
     * @return
     */
    public static String vcode(){
        String vcode = "";
        for (int i = 0; i < 7; i++) {
            vcode = vcode + (int)(Math.random() * 9);
        }
        return vcode;
    }
    @RequestMapping("/register")
    @Transactional(rollbackFor = Exception.class)
    public Result register(@RequestBody Map<String, Object> map){

        String name = map.get("name").toString();
        String password = map.get("password").toString();
        String verifyCode = map.get("verifyCode").toString();
        //3.获取生成的验证码信息
        String uuid = map.get("uuid").toString();
        if (redisUtil.get("verifyCode:"+uuid) != null) {
            String key = "verifyCode:" + uuid;
            String verifyCode_redis = redisUtil.get(key).toString().toLowerCase();
            //由于redis存储了验证码信息,所以当跳转后在浏览器点返回时候,不改变验证码又可以登录了.所以要在获取后立即删除
            redisUtil.delete(key);
            if (verifyCode != null && verifyCode.toLowerCase().equals(verifyCode_redis)) {
                //短信验证注释
//                String smsCode = map.get("smsCode").toString();
//                Map<String, String>  codeMap = redisUtil.getMap("code:"+name);
//                if (codeMap != null && codeMap.get(name).equals(smsCode)){
//                    redisUtil.delete("code:"+name);
//                }else{
//                    return ResultUtil.error(500,"短信验证码错误！");
//                }
                SysUserEntity userEntity = userService.getUserByName(name);
                List<Map<String, Object>> mapList = userService.getByCode(map);
                Map<String, Object> inviteMap = new HashMap<>();
                String pid = null;
                for (Map<String, Object> objectMap : mapList) {
                    if (objectMap.get("code").toString().equals(map.get("code").toString())){
                        pid = objectMap.get("id").toString();
                    }
                    inviteMap.put(objectMap.get("code").toString(),objectMap.get("code"));
                }
                //如果mapUser不为空，已经注册
                if(userEntity!=null) {
                    return ResultUtil.error(500,"该手机号已注册");
                }else {//否则用户不存在
                    if (StringUtils.isNotBlank(pid)){
                        boolean status = true;
                        while(status){
                            String code = "KP"+vcode();
                            if (inviteMap.get(code) == null){
                                status = false;
                                map.put("code", code);
                            }
                        }
                        //获取盐值
                        String salt = SHA256Util.getSalt();
                        map.put("salt",salt);
                        map.put("password",SHA256Util.sha256(password,salt));
                        map.put("id", IDTool.getUUID32());
                        map.put("pid", pid);
                        userService.saveRegister(map);
                        Map<String, Object> mapPayInfo = new HashMap<>();
                        mapPayInfo.put("id",map.get("id"));
                        mapPayInfo.put("nc",map.get("nikeName"));
                        payInfoService.save(mapPayInfo);
                        commissionService.save(mapPayInfo);
                        return ResultUtil.success();
                    }else{
                        return ResultUtil.error(500,"邀请码有误");
                    }

                }
            }else{
                return ResultUtil.error(500,"验证码错误");
            }
        }else{
            return ResultUtil.error(500,"验证码错误");
        }

    }
    /**
     * @description: 修改登录密码
     * @param
     * @return: com.example.utils.Result
     * @author: xxx
     * @time: 2022/6/8 15:11
     */
    @RequestMapping("/updatePassword")
    public Result updatePassword(@RequestBody Map<String, Object> map) {

        String password = map.get("password").toString();
        String passwordNew = map.get("passwordNew").toString();
        Map<String, Object> mapNew = new HashMap<>();
        mapNew.put("id",TokenTool.getUserId());
        Map<String, Object> result = userService.getById(mapNew);
        if (SHA256Util.sha256(password,result.get("salt").toString()).equals(result.get("password").toString())){
            //获取盐值
            String salt = SHA256Util.getSalt();
            mapNew.put("salt",salt);
            mapNew.put("password",SHA256Util.sha256(passwordNew,salt));
            userService.update(mapNew);
            return ResultUtil.success();
        }else{
            return ResultUtil.error(-500,"原登陆密码错误！");
        }
    }
    @RequestMapping("/getVerifyCode")
    public Result getVerificationCode(HttpServletResponse response) {
        Map<String, Object> result = new HashMap<String, Object>();
        try {
            int width=200;
            int height=69;
            //生成对应宽高的初始图片
            BufferedImage verifyImg=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
            //单独的一个类方法，出于代码复用考虑，进行了封装。
            String randomText = VerifyCode.drawRandomText(width,height,verifyImg);
            //功能是生成验证码字符并加上噪点，干扰线，返回值为验证码字符
            response.setContentType("image/png");//必须设置响应内容类型为图片，否则前台不识别
            //将图片转换陈字符串给前端
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            ImageIO.write(verifyImg, "png", stream);
            String base64 = Base64.encode(stream.toByteArray());
            stream.flush();
            stream.close();
            //封装数据
            String uuid = IDTool.getUUID32();
            result.put("image", "data:image/png;base64,"+base64);
            result.put("uuid", uuid);
            redisUtil.set("verifyCode:"+uuid,randomText.toLowerCase(),5L, TimeUnit.MINUTES);


        } catch (IOException e) {
            e.printStackTrace();

        }
        return ResultUtil.success(result);
    }
    @RequestMapping("updatePassWord")
    public Result updatePassWord(@RequestBody Map<String,Object> map) {
        String name = map.get("name").toString();
        String password = map.get("password").toString();
        String verifyCode = map.get("verifyCode").toString();
        //3.获取生成的验证码信息
        String uuid = map.get("uuid").toString();
        if (redisUtil.get("verifyCode:"+uuid) != null) {
            String key = "verifyCode:" + uuid;
            String verifyCode_redis = redisUtil.get(key).toString().toLowerCase();
            //由于redis存储了验证码信息,所以当跳转后在浏览器点返回时候,不改变验证码又可以登录了.所以要在获取后立即删除
            redisUtil.delete(key);
            if (verifyCode != null && verifyCode.toLowerCase().equals(verifyCode_redis)) {
                String code = map.get("smsCode").toString();
                Map<String, String>  codeMap = redisUtil.getMap("code:"+name);
                if (codeMap != null && codeMap.get(name).equals(code)){
                    redisUtil.delete("code:"+name);
                }else{
                    return ResultUtil.error(500,"短信验证码错误！");
                }
                Map<String,Object> user = new HashMap<>();
                user.put("name",name);
                //获取盐值
                String salt = SHA256Util.getSalt();
                user.put("salt",salt);
                user.put("password",SHA256Util.sha256(password,salt));
                userService.updateByName(user);
                return ResultUtil.success();

            }else{
                return ResultUtil.error(500,"验证码错误");
            }
        }else{
            return ResultUtil.error(500,"验证码错误");
        }
    }
    /**
     * @description: 修改支付密码
     * @param map
     * @return: com.example.utils.Result
     * @author: xxx
     * @time: 2022/6/8 18:08
     */
    @RequestMapping("/updatePaymentCode")
    public Result updatePaymentCode(@RequestBody Map<String, Object> map) {

        String paymentCode = map.get("paymentCode").toString();
        String paymentCodeNew = map.get("paymentCodeNew").toString();
        Map<String, Object> mapNew = new HashMap<>();
        mapNew.put("id",TokenTool.getUserId());
        Map<String, Object> result = userService.getById(mapNew);
        if (DigestUtils.md5DigestAsHex(paymentCode.getBytes()).equals(result.get("paymentCode").toString())){
            mapNew.put("paymentCode", DigestUtils.md5DigestAsHex(paymentCodeNew.getBytes()));
            userService.update(mapNew);
            return ResultUtil.success();
        }else{
            return ResultUtil.error(-500,"原支付密码错误！");
        }
    }
    /**
     * @description: 重置密码
     * @param map
     * @return: com.example.utils.Result
     * @author: xxx
     * @time: 2022/6/8 18:08
     */
    @RequestMapping("/resetPaymentCode")
    public Result resetPassword(@RequestBody Map<String, Object> map) {

        String password = map.get("password").toString();
        String paymentCode = map.get("paymentCode").toString();
        Map<String, Object> mapNew = new HashMap<>();
        mapNew.put("id",TokenTool.getUserId());
        Map<String, Object> result = userService.getById(mapNew);
        if (SHA256Util.sha256(password,result.get("salt").toString()).equals(result.get("password").toString())){
            mapNew.put("paymentCode", DigestUtils.md5DigestAsHex(paymentCode.getBytes()));
            userService.update(mapNew);
            return ResultUtil.success();
        }else{
            return ResultUtil.error(-500,"原登陆密码错误！");
        }
    }
    @RequestMapping("/signature")
    public Result signature(@RequestBody Map<String, Object> map) {
        map.put("id",TokenTool.getUserId());
        String signature = map.get("signature").toString();
        String imgFilePath=parentPath+DigestUtils.md5DigestAsHex(IDTool.getUUID32().getBytes()) +  ".jpg";
        if (GenerateImage(signature,imgFilePath)){
            String  uploadUrl = imgFilePath.replace(parentPath,url);
            map.put("signature",uploadUrl);
            userService.update(map);
            return ResultUtil.success(uploadUrl);
        }else{
            return ResultUtil.error(-500,"签名错误！");
        }
    }
    /**
     * @description: 获取签名
     * @param map
     * @return: com.example.utils.Result
     * @author: xxx
     * @time: 2022/10/16 10:54
     */
    @RequestMapping("/getSignature")
    public Result getSignature(@RequestBody Map<String, Object> map) {

        map.put("id",TokenTool.getUserId());
        Map<String, Object> result = userService.getById(map);
        if (result.get("signature") != null && StringUtils.isNotBlank(result.get("signature").toString())){
            return ResultUtil.success(true);
        }else{
            return ResultUtil.success(false);
        }

    }
    @RequestMapping("/sendCode")
    public Result sendCode(@RequestBody Map<String, Object> map) throws Exception{

        String phone = map.get("phone").toString();
        //请求Headers中的X-WSSE参数值
        String wsseHeader = buildWsseHeader(appKey, appSecret);
        String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1";
        String sender = "8822110212295"; //国内短信签名通道号或国际/港澳台短信通道号
        String templateId = "f4e73eafb00b41d88389969ea459f5ac"; //模板ID
        String signature = "沁玉阁"; //签名名称
        String receiver = "+86"+phone; //短信接收人号码

        String statusCallBack = "";
        String randNum = VerificatieCodeUtils.getRandNum(6);
        String[] templateParas = new String[]{randNum};
//        String templateParas = "[\"369752\"]";
        //请求Body,不携带签名名称时,signature请填null
        String body = buildRequestBody(sender, receiver, templateId, JSONObject.toJSONString(templateParas), statusCallBack, signature);

        CloseableHttpClient client = HttpClients.custom()
                .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null,
                        (x509CertChain, authType) -> true).build())
                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                .build();

        //请求方法POST
        HttpResponse response = client.execute(RequestBuilder.create("POST")
                .setUri(url)
                .addHeader(org.apache.http.HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
                .addHeader(org.apache.http.HttpHeaders.AUTHORIZATION, AUTH_HEADER_VALUE)
                .addHeader("X-WSSE", wsseHeader)
                .setEntity(new StringEntity(body)).build());
        //打印响应消息实体
        String entity = EntityUtils.toString(response.getEntity());
        HwSmsRoot hwSmsRoot = JSONObject.parseObject(entity, HwSmsRoot.class);
        if (hwSmsRoot.getCode().equals("000000")){
            for (String templatePara : templateParas) {
                Map<String, String> mapCode = new HashMap<>();
                mapCode.put(phone,templatePara);
                redisUtil.setMap("code:"+phone,mapCode,5L, TimeUnit.MINUTES);//设置失效时间为5分钟
            }

        }

        return ResultUtil.success(hwSmsRoot);
    }
    @RequestMapping("update")
    public Result update(@RequestBody Map<String,Object> map) {
        map.put("id",TokenTool.getUserId());
        userService.update(map);
        return ResultUtil.success();

    }
    public static boolean GenerateImage(String base64Str, String imgFilePath) {
        if (base64Str == null) // 图像数据为空
            return false;
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            // Base64解码
            byte[] bytes = decoder.decodeBuffer(base64Str);
            for (int i = 0; i < bytes.length; ++i) {
                if (bytes[i] < 0) {// 调整异常数据
                    bytes[i] += 256;
                }
            }
            // 生成jpeg图片
            OutputStream out = new FileOutputStream(imgFilePath);
            out.write(bytes);
            out.flush();
            out.close();
            //====
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * 构造X-WSSE参数值
     * @param appKey
     * @param appSecret
     * @return
     */
    static String buildWsseHeader(String appKey, String appSecret) {
        if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
            System.out.println("buildWsseHeader(): appKey or appSecret is null.");
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        String time = sdf.format(new Date()); //Created
        String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce

        MessageDigest md;
        byte[] passwordDigest = null;

        try {
            md = MessageDigest.getInstance("SHA-256");
            md.update((nonce + time + appSecret).getBytes());
            passwordDigest = md.digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        //如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
        String passwordDigestBase64Str = java.util.Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
        //如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码
        //String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest
        //若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正
        //passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", "");
        return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
    }
    /**
     * 构造请求Body体
     * @param sender
     * @param receiver
     * @param templateId
     * @param templateParas
     * @param statusCallBack
     * @param signature | 签名名称,使用国内短信通用模板时填写
     * @return
     */
    static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
                                   String statusCallBack, String signature) {
        if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
                || templateId.isEmpty()) {
            System.out.println("buildRequestBody(): sender, receiver or templateId is null.");
            return null;
        }
        Map<String, String> map = new HashMap<String, String>();

        map.put("from", sender);
        map.put("to", receiver);
        map.put("templateId", templateId);
        if (null != templateParas && !templateParas.isEmpty()) {
            map.put("templateParas", templateParas);
        }
        if (null != statusCallBack && !statusCallBack.isEmpty()) {
            map.put("statusCallback", statusCallBack);
        }
        if (null != signature && !signature.isEmpty()) {
            map.put("signature", signature);
        }

        StringBuilder sb = new StringBuilder();
        String temp = "";

        for (String s : map.keySet()) {
            try {
                temp = URLEncoder.encode(map.get(s), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            sb.append(s).append("=").append(temp).append("&");
        }

        return sb.deleteCharAt(sb.length()-1).toString();
    }
}
